package fr.upmc.aladyn.transactionables.saveobject;

import java.util.HashMap;
import java.util.Stack;


/**
 * <p>Singleton used to manage all the SaveRestoreObjects by stacking them into StackOfSaveRestoreObjects.</p>
 * @author Alexis Deluze
 *
 */
public class SaveManager {
	private static SaveManager saveManager;
	private static HashMap<Long, Stack<StackOfSaveRestoreObjects>> hashMapSaveManager;
	
	/**
	 * <p>Private Constructor</p>
	 */
	private SaveManager(){};

	/**
	 * <p>Singleton to get the StackManager.</p>
	 * <p>A new {@link StackOfSaveRestoreObjects} with the Thread's id is created if it doesn't exist in the StackManager.</p>
	 * @return the SaveManager
	 */
	public static synchronized SaveManager getManager() {
		long threadId = Thread.currentThread().getId();
		if (saveManager == null) {
			saveManager = new SaveManager();
			System.out.println("[SAVE MANAGER] Creation du SaveManager par le Thread "+threadId);
			hashMapSaveManager = new HashMap<Long, Stack<StackOfSaveRestoreObjects>>();
		}
		if (! hashMapSaveManager.containsKey(threadId)){
			System.out.println("[SAVE MANAGER "+threadId+"] Ajout d'une pile dans le SaveManager pour le Thread "+threadId);
			hashMapSaveManager.put(threadId, new Stack<StackOfSaveRestoreObjects>());
		}
		return saveManager;
	}

	/**
	 * <p>Create a new element for a {@link StackOfSaveRestoreObjects}</p>
	 */
	public synchronized void newStackBlock() {
		long threadId = Thread.currentThread().getId();
		StackOfSaveRestoreObjects setSRO = new StackOfSaveRestoreObjects();
		hashMapSaveManager.get(threadId).push(setSRO);
		System.out.println("[SAVE MANAGER "+threadId+"] Debut de methode : ajout d'un bloc de sauvegarde ("+hashMapSaveManager.get(threadId).size()+" blocs existants)");
	}

	/**
	 * <p>Delete the last element of a {@link StackOfSaveRestoreObjects}</p>
	 */
	public synchronized void pop() {
		long threadId = Thread.currentThread().getId();
		hashMapSaveManager.get(threadId).pop();
		System.out.println("[SAVE MANAGER "+threadId+"] Fin de methode : supression d'un bloc de sauvegarde ("+hashMapSaveManager.get(threadId).size()+" restants)");
	}

	/**
	 * <p>Restore all the <code>Objects</code> previously saved into the {@link StackOfSaveRestoreObjects}</p>
	 */
	public synchronized void restore() {
		long threadId = Thread.currentThread().getId();
		System.out.println("[SAVE MANAGER "+threadId+"] Restauration");
		while(! hashMapSaveManager.get(threadId).lastElement().getStack().isEmpty()){
			hashMapSaveManager.get(threadId).lastElement().removeLast().restore();
		}
	}

	/**
	 * <p>Save an <code>Object</code> into all the {@link StackOfSaveRestoreObjects} of the Thread's Stack</p>
	 * @param o The Object to save
	 */
	public synchronized void save(Object o) {
		long threadId = Thread.currentThread().getId();
		if(hashMapSaveManager.get(threadId).size() > 0){
			for(int i = 0; i < hashMapSaveManager.get(threadId).size(); i++){
				SaveRestoreObject sro = new SaveRestoreObject(o);
				sro.save();
				hashMapSaveManager.get(threadId).get(i).add(sro);
			}
		}
	}
	
	/**
	 * <p>Destroy the StackManager (used for JUnit tests)</p>
	 */
	public void destroy(){
		saveManager = null;
	}
}
